package net.sos.translator;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.util.Locale;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;

/**
 * The implementation class which makes use of the Yahoo[Babelfish] translation
 * API to do the translation.
 * 
 * @author SANJAY
 *
 */
public class YahooTranslatorImpl implements ILocaleTranslator {

   private static final String RESULT_DIV_START = "<div id=\"result\"><div style=\"padding:0.6em;\">";
   
   private static final String RESULT_DIV_END = "</div></div>";
   
   private static final String URL = "http://babelfish.yahoo.com/translate_txt";
   
   private Locale fromLocale;
   
   private Locale toLocale;
      
   public YahooTranslatorImpl() {
      super();   
   }

   public YahooTranslatorImpl(Locale fromLocale, Locale toLocale) {
      super();
      this.fromLocale = fromLocale;
      this.toLocale = toLocale;
   }

   /* (non-Javadoc)
    * @see net.sos.translator.ILocaleTranslator#getFromLocale()
    */
   @Override
   public Locale getFromLocale() {      
      return fromLocale;
   }

   /* (non-Javadoc)
    * @see net.sos.translator.ILocaleTranslator#getToLocale()
    */
   @Override
   public Locale getToLocale() {
      return toLocale;
   }

   /* (non-Javadoc)
    * @see net.sos.translator.ILocaleTranslator#setFromLocale(java.util.Locale)
    */
   @Override
   public void setFromLocale(Locale fromLocale) {
      this.fromLocale = fromLocale;
   }

   /* (non-Javadoc)
    * @see net.sos.translator.ILocaleTranslator#setToLocale(java.util.Locale)
    */
   @Override
   public void setToLocale(Locale toLocale) {
      this.toLocale = toLocale;
   }

   /* (non-Javadoc)
    * @see net.sos.translator.ITranslator#translate(java.lang.String)
    */
   @Override
   public String translate(String text) {
      PostMethod post = null;
      try {
         HttpClient client = new HttpClient();
         post = new PostMethod(URL);
         setRequestHeaders(post);
         setRequestBody(post, text);
         client.executeMethod(post);
         String translated = parseTranslatedText(post.getResponseBodyAsStream());
         return translated;
      } catch(Exception e) {
         throw new TranslationException(e.getMessage(), e);        
      } finally {
         // Why is this creating a warning message when it is
         // shown the same way in tutorial?
         //if(post != null)   post.releaseConnection();
      }
   }

   /**
    * Parse the translated text from the HTTP response retrieved as an <code>InputStream</code>.
    * 
    * @param in The HTTP Response stream
    * @return The Translated text
    */
   private String parseTranslatedText(InputStream in) {
      try {
         BufferedReader reader = new BufferedReader(new InputStreamReader(in));
         String line = null;
         int startIdx, endIdx;
         while((line = reader.readLine()) != null) {            
            if((startIdx = line.indexOf(RESULT_DIV_START)) > -1) {
               endIdx = line.indexOf(RESULT_DIV_END);
               line = line.substring(startIdx + RESULT_DIV_START.length(), endIdx);
               break;
            }
         }
         // If we have a NULL here means that the entire response was parsed and no
         // translated text was found; this most probably means we messed up with
         // the locales. Throw a TranslationException.
         if(line == null) {
            throw new TranslationException("Invalid FROM/TO locales specified");
         }
         return line;
      } catch(Exception e) {
         throw new TranslationException(e.getMessage(), e);
      }
   }
   
   private String createRequestLocale() {
      return fromLocale.getLanguage() + "_" + toLocale.getLanguage();
   }

   private void setRequestHeaders(HttpMethod method) {
      Header[] headers = {
            new Header("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11"),
//            new Header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
//            new Header("Accept-Language", "en-us,en;q=0.5"),
//            new Header("Accept-Encoding", "gzip,deflate"),
//            new Header("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"),
//            new Header("Keep-Alive", "300"),
//            new Header("Connection", "keep-alive"),
//            new Header("Referer", "http://babelfish.yahoo.com/"),
      };
      for(Header header : headers) {
         method.setRequestHeader(header);
      }
   }
   
   private void setRequestBody(PostMethod method, String text) {
      NameValuePair[] nvp;
      try {
           nvp = new NameValuePair[] {
           new NameValuePair("ei", "UTF-8"),
           new NameValuePair("doit", "done"),
           new NameValuePair("fr", "bf-home"),
           new NameValuePair("intl", "1"),
           new NameValuePair("tt", "urltext"),
           // You need NOT encode the data to be sent; the encoding is taken care by the library.
           // Come to think of it, encoding here will just cause the translation to fail since
           // encoding Hello+World seems a bit difficult IMO ;-)
           // new NameValuePair("trtext", URLEncoder.encode(text, "UTF-8")),
           new NameValuePair("trtext", text),
           new NameValuePair("lp", createRequestLocale()),
           new NameValuePair("btnTrTxt", "Translate")                               
         };       
      } catch(Exception e) {
         throw new TranslationException(e.getMessage(), e);
      }
      method.setRequestBody(nvp);
   }
   
}
